package com.etone.smartAudit.config;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.etone.smartAudit.apiIntercept.ApiConfig;
import com.etone.smartAudit.domain.Admin;
import com.etone.smartAudit.domain.AdminPermission;
import com.etone.smartAudit.mapper.AdminMapper;
import com.etone.smartAudit.mapper.AdminPermissionMapper;
import com.etone.smartAudit.service.AdminMenuService;
import com.etone.smartAudit.service.AdminPermissionService;
import com.etone.smartAudit.service.AdminRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.support.ServletContextApplicationContextInitializer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.handler.RequestMatchResult;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author: sovy
 * @date: 2020/7/28
 * @description: 自定义权限验证
 */
@Component
@Slf4j
public class CustomSecurity {

    @Autowired
    private AdminPermissionMapper adminPermissionMapper;

    @Autowired
    private AdminMapper adminMapper;

    @Value("${server.servlet.context-path}")
    private String serverPath;

    @Autowired
    private ApiConfig apiConfig;

    @Autowired
    private AdminMenuService adminMenuService;

    @Autowired
    private AdminPermissionService permissionService;

    @Autowired
    private AdminRoleService adminRoleService;

    @Autowired
    private WebApplicationContext applicationContext;

    /**
     * 校验url是否有效
     *
     * @param authentication
     * @param request
     * @return
     */
    public boolean check(Authentication authentication, HttpServletRequest request) {
        apiConfig.checkParam(request);
        Object principalObject = authentication.getPrincipal();
        if (principalObject != null && principalObject instanceof UserDetails) {
            UserDetails principal = (UserDetails) principalObject;
            Admin admin = adminMapper.findByAccount(principal.getUsername());
            if (admin.isAdmin()) {
                //超级管理员拥有所有权限
                return true;
            }

            //h5请求，过滤掉权限
            String header = request.getHeader("wapheader");

            if (StrUtil.isNotEmpty(header)) {
                return true;
            }
            String mehodName = getUrlName(request);
            if (StrUtil.isEmpty(mehodName)) {
                log.error("获取不到接口名称");
                return false;
            }

            //获取请求登录的url
            String menuId = request.getHeader("headMenu");
            String method = request.getMethod();
            String url = request.getRequestURI().replaceFirst(serverPath, "");
            if (url.endsWith("/")) {
                url = url.substring(0, url.length() - 1);
            }
            if ("/auth/logout".equals(url) || "/auth/login".equals(url)) {
                //退出登录不需要权限
                return true;
            }

            if (!url.equals("/admins/menus")) {
                //获取当前的菜单id
                if (StrUtil.isEmpty(menuId)) {
                    return false;
                }
            } else {
                return true;
            }
            //需要判断菜单了
            List<AdminPermission> adminPermissions = adminPermissionMapper.findByNameAndMethod(mehodName, method);
            if (CollectionUtils.isEmpty(adminPermissions)) {
                log.error("获取不到权限:{}", url);
                return false;
            }
            //如果在该该菜单下有调用则添加到该菜单下面  然后判断是否有这个权限
            //获取该接口的该权限
            return handPermissionAndMenu(adminPermissions, menuId, admin.getRoleId(), url);

        }
        return false;
    }

    /***
     * 处理菜单和权限
     */
    private boolean handPermissionAndMenu(List<AdminPermission> adminPermission, String menuId, Integer roleId, String url) {
        //获取该角色是否拥有该菜单
        Integer count = adminMenuService.getMenuByRoleIdAndMenuId(Integer.valueOf(menuId), roleId);
        if (count <= 0) {
            return false;
        }

        //判断该权限是否已经添加到了该菜单下面,不存在则添加
        adminPermission.stream().forEach(permission -> {
            if (permission.getUrl().contains("dataModelTypes/tree")) {
                log.error("-----------");
            }
            permissionService.insertMenuPermission(Integer.valueOf(menuId), permission.getId());
        });
//        //获取角色的权限
////        adminRoleService
        List<Map<String, Object>> maps = adminRoleService.getmenuIdsByRoleIdAndPermissionId(roleId, adminPermission.get(0).getId());
        if (CollectionUtils.isEmpty(maps)) {
            return false;
        }
        AtomicBoolean flag = new AtomicBoolean(false);
        maps.stream().forEach(vMap -> {
            try {
                String menuIds = vMap.get("vcParentIds").toString();
                String[] mIds = menuIds.split(",");
                if (Arrays.asList(mIds).contains(menuId)) {
                    flag.set(true);
                    return;
                }
            } catch (Exception e) {
                log.error("检验权限错误", e);
            }
        });
        return flag.get();
    }

    private String getUrlName(HttpServletRequest request) {
        String realMethod = request.getMethod();
        String realUrl = request.getRequestURI().replaceFirst(serverPath, "");
        // 获取springmvc处理器映射器组件对象 RequestMappingHandlerMapping无法直接注入
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        try {
            HandlerExecutionChain handler = mapping.getHandler(request);
            HandlerMethod handlerMethod = (HandlerMethod) handler.getHandler();
            Method method = handlerMethod.getResolvedFromHandlerMethod().getMethod();
            Annotation[] annotations = method.getDeclaredAnnotations();
            String mapperName = "";
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().equals(GetMapping.class)) {
                    GetMapping reqmapper = (GetMapping) annotation;
                    mapperName = reqmapper.value()[0];
                    break;
                } else if (annotation.annotationType().equals(PostMapping.class)) {
                    PostMapping reqmapper = (PostMapping) annotation;
                    mapperName = reqmapper.value()[0];
                    break;
                } else if (annotation.annotationType().equals(PutMapping.class)) {
                    PutMapping reqmapper = (PutMapping) annotation;
                    mapperName = reqmapper.value()[0];
                    break;
                } else if (annotation.annotationType().equals(DeleteMapping.class)) {
                    DeleteMapping reqmapper = (DeleteMapping) annotation;
                    mapperName = reqmapper.value()[0];
                    break;
                } else if (annotation.annotationType().equals(RequestMapping.class)) {
                    RequestMapping reqmapper = (RequestMapping) annotation;
                    mapperName = reqmapper.value()[0];
                    break;
                } else if (annotation.annotationType().equals(PatchMapping.class)) {
                    PatchMapping reqmapper = (PatchMapping) annotation;
                    mapperName = reqmapper.value()[0];
                    break;
                }
            }
            String vcName = realMethod + mapperName.replace("/", "_").replaceAll("\\{", "").replaceAll("}", "");
            return vcName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
